package basicutil.dynamic;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.ForwardingJavaFileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class JdkDynamicCompileJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
    private final JdkDynamicCompileClassLoader classLoader;

    public JdkDynamicCompileJavaFileManager(JavaFileManager fileManager, JdkDynamicCompileClassLoader classLoader) {
        super(fileManager);
        this.classLoader = classLoader;
    }

    /**
     * 返回的是编译之后的class文件，会对应一个具体的文件
     * javac会调用openOutputStream方法往文件里面写编出来的字节流,最终落盘成class文件，放到源文件同级目录
     * 重写openOutputStream、openInputStream可以不用生成class文件，保存到内存即可
     */
    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
        JavaFileObject javaFileForOutput = super.getJavaFileForOutput(location, className, kind, sibling);

        ForwardingJavaFileObject<JavaFileObject> obj = new ForwardingJavaFileObject<JavaFileObject>(javaFileForOutput) {
            private ByteArrayOutputStream byteCode;

            @Override
            public InputStream openInputStream() {
                return new ByteArrayInputStream(getByteCode());
            }

            // 注意这个方法是编译结果回调的OutputStream，回调成功后就能通过下面的getByteCode()方法获取目标类编译后的字节码字节数组
            @Override
            public OutputStream openOutputStream() {
                return byteCode = new ByteArrayOutputStream();
            }

            public byte[] getByteCode() {
                return byteCode.toByteArray();
            }
        };
        classLoader.addJavaFileObject(className, obj);
        return obj;
    }

    /**
     * 这里覆盖原来的类加载器, 可以让jdk编译源文件的时候使用指定类加载器处理源文件，
     * 如果不指定，可能产生找不到啊符号等错误，调用点：com.sun.tools.javac.processing.JavacProcessingEnvironment#initProcessorClassLoader()
     * 上面这两句话是我的猜测
     * <p>
     * 按照方法上的注释来看这应该是是用来加载注册处理器的，和源文件的依赖没有半毛钱关系
     * 真要排除那个报错应该在list方法实现，把classpath添加进去
     * 而且
     * <p>
     * ！！！！
     * 坑爹的是
     * 编译完成后他会把classLoader关了，然后就会导致加载不到源文件依赖的类，debug到怀疑人生，真尼玛坑爹
     */
    @Override
    public ClassLoader getClassLoader(Location location) {
        return super.getClassLoader(location);
//        return classLoader;
    }

    /**
     * 没事别瞎调用！！！！！！
     * 编译完成之后再调用！！！！！！！
     * 编译前调用会导致不正常的初始化，坑爹！！！！！
     */
    @Override
    public String inferBinaryName(Location location, JavaFileObject file) {
        return super.inferBinaryName(location, file);
    }

}